home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tcl8.0 / unix / tclUnixChan.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  69.6 KB  |  2,564 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tclUnixChan.c
  3.  *
  4.  *    Common channel driver for Unix channels based on files, command
  5.  *    pipes and TCP sockets.
  6.  *
  7.  * Copyright (c) 1995-1997 Sun Microsystems, Inc.
  8.  *
  9.  * See the file "license.terms" for information on usage and redistribution
  10.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  11.  *
  12.  * SCCS: @(#) tclUnixChan.c 1.203 97/06/20 13:03:18
  13.  */
  14.  
  15. #include    "tclInt.h"    /* Internal definitions for Tcl. */
  16. #include    "tclPort.h"    /* Portability features for Tcl. */
  17.  
  18. /*
  19.  * sys/ioctl.h has already been included by tclPort.h.  Including termios.h
  20.  * or termio.h causes a bunch of warning messages because some duplicate
  21.  * (but not contradictory) #defines exist in termios.h and/or termio.h
  22.  */
  23. #undef NL0
  24. #undef NL1
  25. #undef CR0
  26. #undef CR1
  27. #undef CR2
  28. #undef CR3
  29. #undef TAB0
  30. #undef TAB1
  31. #undef TAB2
  32. #undef XTABS
  33. #undef BS0
  34. #undef BS1
  35. #undef FF0
  36. #undef FF1
  37. #undef ECHO
  38. #undef NOFLSH
  39. #undef TOSTOP
  40. #undef FLUSHO
  41. #undef PENDIN
  42.  
  43. #ifdef USE_TERMIOS
  44. #   include <termios.h>
  45. #else    /* !USE_TERMIOS */
  46. #ifdef USE_TERMIO
  47. #   include <termio.h>
  48. #else    /* !USE_TERMIO */
  49. #ifdef USE_SGTTY
  50. #   include <sgtty.h>
  51. #endif    /* USE_SGTTY */
  52. #endif    /* !USE_TERMIO */
  53. #endif    /* !USE_TERMIOS */
  54.  
  55. /*
  56.  * The following structure is used to set or get the serial port
  57.  * attributes in a platform-independant manner.
  58.  */
  59.  
  60. typedef struct TtyAttrs {
  61.     int baud;
  62.     int parity;
  63.     int data;
  64.     int stop;
  65. } TtyAttrs;
  66.  
  67. /*
  68.  * This structure describes per-instance state of a file based channel.
  69.  */
  70.  
  71. typedef struct FileState {
  72.     Tcl_Channel channel;    /* Channel associated with this file. */
  73.     int fd;            /* File handle. */
  74.     int validMask;        /* OR'ed combination of TCL_READABLE,
  75.                  * TCL_WRITABLE, or TCL_EXCEPTION: indicates
  76.                  * which operations are valid on the file. */
  77.     struct FileState *nextPtr;    /* Pointer to next file in list of all
  78.                  * file channels. */
  79. } FileState;
  80.  
  81. /*
  82.  * List of all file channels currently open.
  83.  */
  84.  
  85. static FileState *firstFilePtr = NULL;
  86.  
  87. /*
  88.  * This structure describes per-instance state of a tcp based channel.
  89.  */
  90.  
  91. typedef struct TcpState {
  92.     Tcl_Channel channel;    /* Channel associated with this file. */
  93.     int fd;            /* The socket itself. */
  94.     int flags;            /* ORed combination of the bitfields
  95.                  * defined below. */
  96.     Tcl_TcpAcceptProc *acceptProc;
  97.                 /* Proc to call on accept. */
  98.     ClientData acceptProcData;    /* The data for the accept proc. */
  99. } TcpState;
  100.  
  101. /*
  102.  * These bits may be ORed together into the "flags" field of a TcpState
  103.  * structure.
  104.  */
  105.  
  106. #define TCP_ASYNC_SOCKET    (1<<0)    /* Asynchronous socket. */
  107. #define TCP_ASYNC_CONNECT    (1<<1)    /* Async connect in progress. */
  108.  
  109. /*
  110.  * The following defines the maximum length of the listen queue. This is
  111.  * the number of outstanding yet-to-be-serviced requests for a connection
  112.  * on a server socket, more than this number of outstanding requests and
  113.  * the connection request will fail.
  114.  */
  115.  
  116. #ifndef    SOMAXCONN
  117. #define SOMAXCONN    100
  118. #endif
  119.  
  120. #if    (SOMAXCONN < 100)
  121. #undef    SOMAXCONN
  122. #define    SOMAXCONN    100
  123. #endif
  124.  
  125. /*
  126.  * The following defines how much buffer space the kernel should maintain
  127.  * for a socket.
  128.  */
  129.  
  130. #define SOCKET_BUFSIZE    4096
  131.  
  132. /*
  133.  * Static routines for this file:
  134.  */
  135.  
  136. static TcpState *    CreateSocket _ANSI_ARGS_((Tcl_Interp *interp,
  137.                 int port, char *host, int server,
  138.                 char *myaddr, int myport, int async));
  139. static int        CreateSocketAddress _ANSI_ARGS_(
  140.                 (struct sockaddr_in *sockaddrPtr,
  141.                 char *host, int port));
  142. static int        FileBlockModeProc _ANSI_ARGS_((
  143.                     ClientData instanceData, int mode));
  144. static int        FileCloseProc _ANSI_ARGS_((ClientData instanceData,
  145.                 Tcl_Interp *interp));
  146. static int        FileGetHandleProc _ANSI_ARGS_((ClientData instanceData,
  147.                     int direction, ClientData *handlePtr));
  148. static int        FileInputProc _ANSI_ARGS_((ClientData instanceData,
  149.                     char *buf, int toRead, int *errorCode));
  150. static int        FileOutputProc _ANSI_ARGS_((
  151.                 ClientData instanceData, char *buf, int toWrite,
  152.                             int *errorCode));
  153. static int        FileSeekProc _ANSI_ARGS_((ClientData instanceData,
  154.                 long offset, int mode, int *errorCode));
  155. static void        FileWatchProc _ANSI_ARGS_((ClientData instanceData,
  156.                     int mask));
  157. static void        TcpAccept _ANSI_ARGS_((ClientData data, int mask));
  158. static int        TcpBlockModeProc _ANSI_ARGS_((ClientData data,
  159.                     int mode));
  160. static int        TcpCloseProc _ANSI_ARGS_((ClientData instanceData,
  161.                 Tcl_Interp *interp));
  162. static int        TcpGetHandleProc _ANSI_ARGS_((ClientData instanceData,
  163.                     int direction, ClientData *handlePtr));
  164. static int        TcpGetOptionProc _ANSI_ARGS_((ClientData instanceData,
  165.                 Tcl_Interp *interp, char *optionName,
  166.                 Tcl_DString *dsPtr));
  167. static int        TcpInputProc _ANSI_ARGS_((ClientData instanceData,
  168.                     char *buf, int toRead,  int *errorCode));
  169. static int        TcpOutputProc _ANSI_ARGS_((ClientData instanceData,
  170.                     char *buf, int toWrite, int *errorCode));
  171. static void        TcpWatchProc _ANSI_ARGS_((ClientData instanceData,
  172.                     int mask));
  173. static int        TtyParseMode _ANSI_ARGS_((Tcl_Interp *interp,
  174.                 CONST char *mode, int *speedPtr, int *parityPtr,
  175.                 int *dataPtr, int *stopPtr));
  176. static void        TtyGetAttributes _ANSI_ARGS_((int fd,
  177.                 TtyAttrs *ttyPtr));
  178. static int        TtyGetOptionProc _ANSI_ARGS_((ClientData instanceData,
  179.                 Tcl_Interp *interp, char *optionName,
  180.                 Tcl_DString *dsPtr));
  181. static void        TtyInit _ANSI_ARGS_((int fd));
  182. static void        TtySetAttributes _ANSI_ARGS_((int fd,
  183.                 TtyAttrs *ttyPtr));
  184. static int        TtySetOptionProc _ANSI_ARGS_((ClientData instanceData,
  185.                 Tcl_Interp *interp, char *optionName, 
  186.                 char *value));
  187. static int        WaitForConnect _ANSI_ARGS_((TcpState *statePtr,
  188.                     int *errorCodePtr));
  189.  
  190. /*
  191.  * This structure describes the channel type structure for file based IO:
  192.  */
  193.  
  194. static Tcl_ChannelType fileChannelType = {
  195.     "file",                /* Type name. */
  196.     FileBlockModeProc,            /* Set blocking/nonblocking mode.*/
  197.     FileCloseProc,            /* Close proc. */
  198.     FileInputProc,            /* Input proc. */
  199.     FileOutputProc,            /* Output proc. */
  200.     FileSeekProc,            /* Seek proc. */
  201.     NULL,                /* Set option proc. */
  202.     NULL,                /* Get option proc. */
  203.     FileWatchProc,            /* Initialize notifier. */
  204.     FileGetHandleProc,            /* Get OS handles out of channel. */
  205. };
  206.  
  207. /*
  208.  * This structure describes the channel type structure for serial IO.
  209.  * Note that this type is a subclass of the "file" type.
  210.  */
  211.  
  212. static Tcl_ChannelType ttyChannelType = {
  213.     "tty",                /* Type name. */
  214.     FileBlockModeProc,            /* Set blocking/nonblocking mode.*/
  215.     FileCloseProc,            /* Close proc. */
  216.     FileInputProc,            /* Input proc. */
  217.     FileOutputProc,            /* Output proc. */
  218.     NULL,                /* Seek proc. */
  219.     TtySetOptionProc,            /* Set option proc. */
  220.     TtyGetOptionProc,            /* Get option proc. */
  221.     FileWatchProc,            /* Initialize notifier. */
  222.     FileGetHandleProc,            /* Get OS handles out of channel. */
  223. };
  224.  
  225. /*
  226.  * This structure describes the channel type structure for TCP socket
  227.  * based IO:
  228.  */
  229.  
  230. static Tcl_ChannelType tcpChannelType = {
  231.     "tcp",                /* Type name. */
  232.     TcpBlockModeProc,            /* Set blocking/nonblocking mode.*/
  233.     TcpCloseProc,            /* Close proc. */
  234.     TcpInputProc,            /* Input proc. */
  235.     TcpOutputProc,            /* Output proc. */
  236.     NULL,                /* Seek proc. */
  237.     NULL,                /* Set option proc. */
  238.     TcpGetOptionProc,            /* Get option proc. */
  239.     TcpWatchProc,            /* Initialize notifier. */
  240.     TcpGetHandleProc,            /* Get OS handles out of channel. */
  241. };
  242.  
  243.  
  244. /*
  245.  *----------------------------------------------------------------------
  246.  *
  247.  * FileBlockModeProc --
  248.  *
  249.  *    Helper procedure to set blocking and nonblocking modes on a
  250.  *    file based channel. Invoked by generic IO level code.
  251.  *
  252.  * Results:
  253.  *    0 if successful, errno when failed.
  254.  *
  255.  * Side effects:
  256.  *    Sets the device into blocking or non-blocking mode.
  257.  *
  258.  *----------------------------------------------------------------------
  259.  */
  260.  
  261.     /* ARGSUSED */
  262. static int
  263. FileBlockModeProc(instanceData, mode)
  264.     ClientData instanceData;        /* File state. */
  265.     int mode;                /* The mode to set. Can be one of
  266.                                          * TCL_MODE_BLOCKING or
  267.                                          * TCL_MODE_NONBLOCKING. */
  268. {
  269.     FileState *fsPtr = (FileState *) instanceData;
  270.     int curStatus;
  271.  
  272. #ifndef USE_FIONBIO
  273.     curStatus = fcntl(fsPtr->fd, F_GETFL);
  274.     if (mode == TCL_MODE_BLOCKING) {
  275.     curStatus &= (~(O_NONBLOCK));
  276.     } else {
  277.     curStatus |= O_NONBLOCK;
  278.     }
  279.     if (fcntl(fsPtr->fd, F_SETFL, curStatus) < 0) {
  280.     return errno;
  281.     }
  282.     curStatus = fcntl(fsPtr->fd, F_GETFL);
  283. #else
  284.     if (mode == TCL_MODE_BLOCKING) {
  285.     curStatus = 0;
  286.     } else {
  287.     curStatus = 1;
  288.     }
  289.     if (ioctl(fsPtr->fd, (int) FIONBIO, &curStatus) < 0) {
  290.     return errno;
  291.     }
  292. #endif
  293.     return 0;
  294. }
  295.  
  296. /*
  297.  *----------------------------------------------------------------------
  298.  *
  299.  * FileInputProc --
  300.  *
  301.  *    This procedure is invoked from the generic IO level to read
  302.  *    input from a file based channel.
  303.  *
  304.  * Results:
  305.  *    The number of bytes read is returned or -1 on error. An output
  306.  *    argument contains a POSIX error code if an error occurs, or zero.
  307.  *
  308.  * Side effects:
  309.  *    Reads input from the input device of the channel.
  310.  *
  311.  *----------------------------------------------------------------------
  312.  */
  313.  
  314. static int
  315. FileInputProc(instanceData, buf, toRead, errorCodePtr)
  316.     ClientData instanceData;        /* File state. */
  317.     char *buf;                /* Where to store data read. */
  318.     int toRead;                /* How much space is available
  319.                                          * in the buffer? */
  320.     int *errorCodePtr;            /* Where to store error code. */
  321. {
  322.     FileState *fsPtr = (FileState *) instanceData;
  323.     int bytesRead;            /* How many bytes were actually
  324.                                          * read from the input device? */
  325.  
  326.     *errorCodePtr = 0;
  327.     
  328.     /*
  329.      * Assume there is always enough input available. This will block
  330.      * appropriately, and read will unblock as soon as a short read is
  331.      * possible, if the channel is in blocking mode. If the channel is
  332.      * nonblocking, the read will never block.
  333.      */
  334.  
  335.     bytesRead = read(fsPtr->fd, buf, (size_t) toRead);
  336.     if (bytesRead > -1) {
  337.         return bytesRead;
  338.     }
  339.     *errorCodePtr = errno;
  340.     return -1;
  341. }
  342.  
  343. /*
  344.  *----------------------------------------------------------------------
  345.  *
  346.  * FileOutputProc--
  347.  *
  348.  *    This procedure is invoked from the generic IO level to write
  349.  *    output to a file channel.
  350.  *
  351.  * Results:
  352.  *    The number of bytes written is returned or -1 on error. An
  353.  *    output argument    contains a POSIX error code if an error occurred,
  354.  *    or zero.
  355.  *
  356.  * Side effects:
  357.  *    Writes output on the output device of the channel.
  358.  *
  359.  *----------------------------------------------------------------------
  360.  */
  361.  
  362. static int
  363. FileOutputProc(instanceData, buf, toWrite, errorCodePtr)
  364.     ClientData instanceData;        /* File state. */
  365.     char *buf;                /* The data buffer. */
  366.     int toWrite;            /* How many bytes to write? */
  367.     int *errorCodePtr;            /* Where to store error code. */
  368. {
  369.     FileState *fsPtr = (FileState *) instanceData;
  370.     int written;
  371.  
  372.     *errorCodePtr = 0;
  373.     written = write(fsPtr->fd, buf, (size_t) toWrite);
  374.     if (written > -1) {
  375.         return written;
  376.     }
  377.     *errorCodePtr = errno;
  378.     return -1;
  379. }
  380.  
  381. /*
  382.  *----------------------------------------------------------------------
  383.  *
  384.  * FileCloseProc --
  385.  *
  386.  *    This procedure is called from the generic IO level to perform
  387.  *    channel-type-specific cleanup when a file based channel is closed.
  388.  *
  389.  * Results:
  390.  *    0 if successful, errno if failed.
  391.  *
  392.  * Side effects:
  393.  *    Closes the device of the channel.
  394.  *
  395.  *----------------------------------------------------------------------
  396.  */
  397.  
  398. static int
  399. FileCloseProc(instanceData, interp)
  400.     ClientData instanceData;    /* File state. */
  401.     Tcl_Interp *interp;        /* For error reporting - unused. */
  402. {
  403.     FileState *fsPtr = (FileState *) instanceData;
  404.     FileState **nextPtrPtr;
  405.     int errorCode = 0;
  406.  
  407.     Tcl_DeleteFileHandler(fsPtr->fd);
  408.     if (!TclInExit()
  409.         || ((fsPtr->fd != 0) && (fsPtr->fd != 1) && (fsPtr->fd != 2))) {
  410.     if (close(fsPtr->fd) < 0) {
  411.         errorCode = errno;
  412.     }
  413.     }
  414.     for (nextPtrPtr = &firstFilePtr; (*nextPtrPtr) != NULL;
  415.      nextPtrPtr = &((*nextPtrPtr)->nextPtr)) {
  416.     if ((*nextPtrPtr) == fsPtr) {
  417.         (*nextPtrPtr) = fsPtr->nextPtr;
  418.         break;
  419.     }
  420.     }
  421.     ckfree((char *) fsPtr);
  422.     return errorCode;
  423. }
  424.  
  425. /*
  426.  *----------------------------------------------------------------------
  427.  *
  428.  * FileSeekProc --
  429.  *
  430.  *    This procedure is called by the generic IO level to move the
  431.  *    access point in a file based channel.
  432.  *
  433.  * Results:
  434.  *    -1 if failed, the new position if successful. An output
  435.  *    argument contains the POSIX error code if an error occurred,
  436.  *    or zero.
  437.  *
  438.  * Side effects:
  439.  *    Moves the location at which the channel will be accessed in
  440.  *    future operations.
  441.  *
  442.  *----------------------------------------------------------------------
  443.  */
  444.  
  445. static int
  446. FileSeekProc(instanceData, offset, mode, errorCodePtr)
  447.     ClientData instanceData;            /* File state. */
  448.     long offset;                /* Offset to seek to. */
  449.     int mode;                    /* Relative to where
  450.                                                  * should we seek? Can be
  451.                                                  * one of SEEK_START,
  452.                                                  * SEEK_SET or SEEK_END. */
  453.     int *errorCodePtr;                /* To store error code. */
  454. {
  455.     FileState *fsPtr = (FileState *) instanceData;
  456.     int newLoc;
  457.  
  458.     newLoc = lseek(fsPtr->fd, offset, mode);
  459.  
  460.     *errorCodePtr = (newLoc == -1) ? errno : 0;
  461.     return newLoc;
  462. }
  463.  
  464. /*
  465.  *----------------------------------------------------------------------
  466.  *
  467.  * FileWatchProc --
  468.  *
  469.  *    Initialize the notifier to watch the fd from this channel.
  470.  *
  471.  * Results:
  472.  *    None.
  473.  *
  474.  * Side effects:
  475.  *    Sets up the notifier so that a future event on the channel will
  476.  *    be seen by Tcl.
  477.  *
  478.  *----------------------------------------------------------------------
  479.  */
  480.  
  481. static void
  482. FileWatchProc(instanceData, mask)
  483.     ClientData instanceData;        /* The file state. */
  484.     int mask;                /* Events of interest; an OR-ed
  485.                                          * combination of TCL_READABLE,
  486.                                          * TCL_WRITABLE and TCL_EXCEPTION. */
  487. {
  488.     FileState *fsPtr = (FileState *) instanceData;
  489.  
  490.     /*
  491.      * Make sure we only register for events that are valid on this file.
  492.      * Note that we are passing Tcl_NotifyChannel directly to
  493.      * Tcl_CreateFileHandler with the channel pointer as the client data.
  494.      */
  495.  
  496.     mask &= fsPtr->validMask;
  497.     if (mask) {
  498.     Tcl_CreateFileHandler(fsPtr->fd, mask,
  499.         (Tcl_FileProc *) Tcl_NotifyChannel,
  500.         (ClientData) fsPtr->channel);
  501.     } else {
  502.     Tcl_DeleteFileHandler(fsPtr->fd);
  503.     }
  504. }
  505.  
  506. /*
  507.  *----------------------------------------------------------------------
  508.  *
  509.  * FileGetHandleProc --
  510.  *
  511.  *    Called from Tcl_GetChannelFile to retrieve OS handles from
  512.  *    a file based channel.
  513.  *
  514.  * Results:
  515.  *    Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
  516.  *    there is no handle for the specified direction. 
  517.  *
  518.  * Side effects:
  519.  *    None.
  520.  *
  521.  *----------------------------------------------------------------------
  522.  */
  523.  
  524. static int
  525. FileGetHandleProc(instanceData, direction, handlePtr)
  526.     ClientData instanceData;    /* The file state. */
  527.     int direction;        /* TCL_READABLE or TCL_WRITABLE */
  528.     ClientData *handlePtr;    /* Where to store the handle.  */
  529. {
  530.     FileState *fsPtr = (FileState *) instanceData;
  531.  
  532.     if (direction & fsPtr->validMask) {
  533.     *handlePtr = (ClientData) fsPtr->fd;
  534.     return TCL_OK;
  535.     } else {
  536.     return TCL_ERROR;
  537.     }
  538. }
  539.  
  540. /*
  541.  *----------------------------------------------------------------------
  542.  *
  543.  * TtySetOptionProc --
  544.  *
  545.  *    Sets an option on a channel.
  546.  *
  547.  * Results:
  548.  *    A standard Tcl result. Also sets interp->result on error if
  549.  *    interp is not NULL.
  550.  *
  551.  * Side effects:
  552.  *    May modify an option on a device.
  553.  *      Sets Error message if needed (by calling Tcl_BadChannelOption).
  554.  *
  555.  *----------------------------------------------------------------------
  556.  */
  557.  
  558. static int        
  559. TtySetOptionProc(instanceData, interp, optionName, value)
  560.     ClientData instanceData;    /* File state. */
  561.     Tcl_Interp *interp;        /* For error reporting - can be NULL. */
  562.     char *optionName;        /* Which option to set? */
  563.     char *value;        /* New value for option. */
  564. {
  565.     FileState *fsPtr = (FileState *) instanceData;
  566.     unsigned int len;
  567.     TtyAttrs tty;
  568.  
  569.     len = strlen(optionName);
  570.     if ((len > 1) && (strncmp(optionName, "-mode", len) == 0)) {
  571.     if (TtyParseMode(interp, value, &tty.baud, &tty.parity, &tty.data,
  572.         &tty.stop) != TCL_OK) {
  573.         return TCL_ERROR;
  574.     }
  575.     /*
  576.      * system calls results should be checked there. -- dl
  577.      */
  578.  
  579.     TtySetAttributes(fsPtr->fd, &tty);
  580.     return TCL_OK;
  581.     } else {
  582.     return Tcl_BadChannelOption(interp, optionName, "mode");
  583.     }
  584. }
  585.  
  586. /*
  587.  *----------------------------------------------------------------------
  588.  *
  589.  * TtyGetOptionProc --
  590.  *
  591.  *    Gets a mode associated with an IO channel. If the optionName arg
  592.  *    is non NULL, retrieves the value of that option. If the optionName
  593.  *    arg is NULL, retrieves a list of alternating option names and
  594.  *    values for the given channel.
  595.  *
  596.  * Results:
  597.  *    A standard Tcl result. Also sets the supplied DString to the
  598.  *    string value of the option(s) returned.
  599.  *
  600.  * Side effects:
  601.  *    The string returned by this function is in static storage and
  602.  *    may be reused at any time subsequent to the call.
  603.  *      Sets Error message if needed (by calling Tcl_BadChannelOption).
  604.  *
  605.  *----------------------------------------------------------------------
  606.  */
  607.  
  608. static int        
  609. TtyGetOptionProc(instanceData, interp, optionName, dsPtr)
  610.     ClientData instanceData;    /* File state. */
  611.     Tcl_Interp *interp;        /* For error reporting - can be NULL. */
  612.     char *optionName;        /* Option to get. */
  613.     Tcl_DString *dsPtr;        /* Where to store value(s). */
  614. {
  615.     FileState *fsPtr = (FileState *) instanceData;
  616.     unsigned int len;
  617.     char buf[32];
  618.     TtyAttrs tty;
  619.  
  620.     if (optionName == NULL) {
  621.     Tcl_DStringAppendElement(dsPtr, "-mode");
  622.     len = 0;
  623.     } else {
  624.     len = strlen(optionName);
  625.     }
  626.     if ((len == 0) || 
  627.         ((len > 1) && (strncmp(optionName, "-mode", len) == 0))) {
  628.     TtyGetAttributes(fsPtr->fd, &tty);
  629.     sprintf(buf, "%d,%c,%d,%d", tty.baud, tty.parity, tty.data, tty.stop);
  630.     Tcl_DStringAppendElement(dsPtr, buf);
  631.     return TCL_OK;
  632.     } else {
  633.     return Tcl_BadChannelOption(interp, optionName, "mode");
  634.     }
  635. }
  636.  
  637. #undef DIRECT_BAUD
  638. #ifdef B4800
  639. #   if (B4800 == 4800)
  640. #    define DIRECT_BAUD
  641. #   endif
  642. #endif
  643.  
  644. #ifdef DIRECT_BAUD
  645. #   define TtyGetSpeed(baud)   ((unsigned) (baud))
  646. #   define TtyGetBaud(speed)   ((int) (speed))
  647. #else
  648.  
  649. static struct {int baud; unsigned long speed;} speeds[] = {
  650. #ifdef B0
  651.     {0, B0},
  652. #endif
  653. #ifdef B50
  654.     {50, B50},
  655. #endif
  656. #ifdef B75
  657.     {75, B75},
  658. #endif
  659. #ifdef B110
  660.     {110, B110},
  661. #endif
  662. #ifdef B134
  663.     {134, B134},
  664. #endif
  665. #ifdef B150
  666.     {150, B150},
  667. #endif
  668. #ifdef B200
  669.     {200, B200},
  670. #endif
  671. #ifdef B300
  672.     {300, B300},
  673. #endif
  674. #ifdef B600
  675.     {600, B600},
  676. #endif
  677. #ifdef B1200
  678.     {1200, B1200},
  679. #endif
  680. #ifdef B1800
  681.     {1800, B1800},
  682. #endif
  683. #ifdef B2400
  684.     {2400, B2400},
  685. #endif
  686. #ifdef B4800
  687.     {4800, B4800},
  688. #endif
  689. #ifdef B9600
  690.     {9600, B9600},
  691. #endif
  692. #ifdef B14400
  693.     {14400, B14400},
  694. #endif
  695. #ifdef B19200
  696.     {19200, B19200},
  697. #endif
  698. #ifdef EXTA
  699.     {19200, EXTA},
  700. #endif
  701. #ifdef B28800
  702.     {28800, B28800},
  703. #endif
  704. #ifdef B38400
  705.     {38400, B38400},
  706. #endif
  707. #ifdef EXTB
  708.     {38400, EXTB},
  709. #endif
  710. #ifdef B57600
  711.     {57600, B57600},
  712. #endif
  713. #ifdef _B57600
  714.     {57600, _B57600},
  715. #endif
  716. #ifdef B76800
  717.     {76800, B76800},
  718. #endif
  719. #ifdef B115200
  720.     {115200, B115200},
  721. #endif
  722. #ifdef _B115200
  723.     {115200, _B115200},
  724. #endif
  725. #ifdef B153600
  726.     {153600, B153600},
  727. #endif
  728. #ifdef B230400
  729.     {230400, B230400},
  730. #endif
  731. #ifdef B307200
  732.     {307200, B307200},
  733. #endif
  734. #ifdef B460800
  735.     {460800, B460800},
  736. #endif
  737.     {-1, 0}
  738. };
  739.  
  740. /*
  741.  *---------------------------------------------------------------------------
  742.  *
  743.  * TtyGetSpeed --
  744.  *
  745.  *    Given a baud rate, get the mask value that should be stored in
  746.  *    the termios, termio, or sgttyb structure in order to select that
  747.  *    baud rate.
  748.  *
  749.  * Results:
  750.  *    As above.
  751.  *
  752.  * Side effects:
  753.  *    None.
  754.  *
  755.  *---------------------------------------------------------------------------
  756.  */
  757.  
  758. static unsigned long
  759. TtyGetSpeed(baud)
  760.     int baud;            /* The baud rate to look up. */
  761. {
  762.     int bestIdx, bestDiff, i, diff;
  763.     
  764.     bestIdx = 0;
  765.     bestDiff = 1000000;
  766.     
  767.     /*
  768.      * If the baud rate does not correspond to one of the known mask values,
  769.      * choose the mask value whose baud rate is closest to the specified
  770.      * baud rate.
  771.      */
  772.  
  773.     for (i = 0; speeds[i].baud >= 0; i++) {
  774.     diff = speeds[i].baud - baud;
  775.     if (diff < 0) {
  776.         diff = -diff;
  777.     }
  778.     if (diff < bestDiff) {
  779.         bestIdx = i;
  780.         bestDiff = diff;
  781.     }
  782.     }
  783.     return speeds[bestIdx].speed;
  784. }
  785.  
  786. /*
  787.  *---------------------------------------------------------------------------
  788.  *
  789.  * TtyGetBaud --
  790.  *
  791.  *    Given a speed mask value from a termios, termio, or sgttyb
  792.  *    structure, get the baus rate that corresponds to that mask value.
  793.  *
  794.  * Results:
  795.  *    As above.  If the mask value was not recognized, 0 is returned.
  796.  *
  797.  * Side effects:
  798.  *    None.
  799.  *
  800.  *---------------------------------------------------------------------------
  801.  */
  802.  
  803. static int
  804. TtyGetBaud(speed)
  805.     unsigned long speed;    /* Speed mask value to look up. */
  806. {
  807.     int i;
  808.     
  809.     for (i = 0; speeds[i].baud >= 0; i++) {
  810.     if (speeds[i].speed == speed) {
  811.         return speeds[i].baud;
  812.     }
  813.     }
  814.     return 0;
  815. }
  816.  
  817. #endif    /* !DIRECT_BAUD */
  818.  
  819.  
  820. /*
  821.  *---------------------------------------------------------------------------
  822.  *
  823.  * TtyInit --
  824.  *
  825.  *    Given file descriptor that refers to a serial port, 
  826.  *    initialize the serial port to a set of sane values so that
  827.  *    Tcl can talk to a device located on the serial port.
  828.  *
  829.  * Results:
  830.  *    None.
  831.  *
  832.  * Side effects:
  833.  *    Serial device initialized.
  834.  *
  835.  *---------------------------------------------------------------------------
  836.  */
  837.  
  838. static void
  839. TtyInit(fd)
  840.     int fd;            /* Open file descriptor for serial port to
  841.                  * be initialized. */
  842. {
  843. #ifdef USE_TERMIOS
  844.     struct termios termios;
  845.  
  846.     tcgetattr(fd, &termios);
  847.     termios.c_iflag = IGNBRK;
  848.     termios.c_oflag = 0;
  849.     termios.c_lflag = 0;
  850.     termios.c_cflag |= CREAD;
  851.     termios.c_cc[VMIN] = 60;
  852.     termios.c_cc[VTIME] = 2;
  853.     tcsetattr(fd, TCSANOW, &termios);
  854. #else    /* !USE_TERMIOS */
  855. #ifdef USE_TERMIO
  856.     struct termio termio;
  857.  
  858.     ioctl(fd, TCGETA, &termio);
  859.     termio.c_iflag = IGNBRK;
  860.     termio.c_oflag = 0;
  861.     termio.c_lflag = 0;
  862.     termio.c_cflag |= CREAD;
  863.     termio.c_cc[VMIN] = 60;
  864.     termio.c_cc[VTIME] = 2;
  865.     ioctl(fd, TCSETAW, &termio);
  866. #else    /* !USE_TERMIO */
  867. #ifdef USE_SGTTY
  868.     struct sgttyb sgttyb;
  869.  
  870.     ioctl(fd, TIOCGETP, &sgttyb);
  871.     sgttyb.sg_flags &= (EVENP | ODDP);
  872.     sgttyb.sg_flags |= RAW;
  873.     ioctl(fd, TIOCSETP, &sgttyb);
  874. #endif    /* USE_SGTTY */
  875. #endif    /* !USE_TERMIO */
  876. #endif    /* !USE_TERMIOS */
  877. }
  878.  
  879. /*
  880.  *---------------------------------------------------------------------------
  881.  *
  882.  * TtyGetAttributes --
  883.  *
  884.  *    Get the current attributes of the specified serial device.
  885.  *
  886.  * Results:
  887.  *    None.
  888.  *
  889.  * Side effects:
  890.  *    None.
  891.  *
  892.  *---------------------------------------------------------------------------
  893.  */
  894.  
  895. static void
  896. TtyGetAttributes(fd, ttyPtr)
  897.     int fd;            /* Open file descriptor for serial port to
  898.                  * be queried. */
  899.     TtyAttrs *ttyPtr;        /* Buffer filled with serial port
  900.                  * attributes. */
  901. {
  902. #ifdef USE_TERMIOS
  903.     int parity, data;
  904.     struct termios termios;
  905.  
  906.     tcgetattr(fd, &termios);
  907.     ttyPtr->baud = TtyGetBaud(cfgetospeed(&termios));
  908.     
  909.     parity = 'n';
  910. #ifdef PAREXT
  911.     switch ((int) (termios.c_cflag & (PARENB | PARODD | PAREXT))) {
  912.     case PARENB              : parity = 'e'; break;
  913.     case PARENB | PARODD          :    parity = 'o'; break;
  914.     case PARENB |           PAREXT : parity = 's'; break;
  915.     case PARENB | PARODD | PAREXT :    parity = 'm'; break;
  916.     }
  917. #else    /* !PAREXT */
  918.     switch ((int) (termios.c_cflag & (PARENB | PARODD))) {
  919.     case PARENB              : parity = 'e'; break;
  920.     case PARENB | PARODD          :    parity = 'o'; break;
  921.     }
  922. #endif    /* !PAREXT */
  923.     ttyPtr->parity = parity;
  924.  
  925.     data = termios.c_cflag & CSIZE;
  926.     ttyPtr->data = (data == CS5) ? 5 : (data == CS6) ? 6 :
  927.         (data == CS7) ? 7 : 8;
  928.  
  929.     ttyPtr->stop = (termios.c_cflag & CSTOPB) ? 2 : 1;
  930. #else    /* !USE_TERMIOS */
  931. #ifdef USE_TERMIO
  932.     int parity, data;
  933.     struct termio termio;
  934.  
  935.     ioctl(fd, TCGETA, &termio);
  936.     ttyPtr->baud = TtyGetBaud(termio.c_cflag & CBAUD);
  937.     parity = 'n';
  938.     switch (termio.c_cflag & (PARENB | PARODD | PAREXT)) {
  939.     case PARENB              : parity = 'e'; break;
  940.     case PARENB | PARODD          :    parity = 'o'; break;
  941.     case PARENB |           PAREXT : parity = 's'; break;
  942.     case PARENB | PARODD | PAREXT :    parity = 'm'; break;
  943.     }
  944.     ttyPtr->parity = parity;
  945.  
  946.     data = termio.c_cflag & CSIZE;
  947.     ttyPtr->data = (data == CS5) ? 5 : (data == CS6) ? 6 :
  948.         (data == CS7) ? 7 : 8;
  949.  
  950.     ttyPtr->stop = (termio.c_cflag & CSTOPB) ? 2 : 1;
  951. #else    /* !USE_TERMIO */
  952. #ifdef USE_SGTTY
  953.     int parity;
  954.     struct sgttyb sgttyb;
  955.  
  956.     ioctl(fd, TIOCGETP, &sgttyb);
  957.     ttyPtr->baud = TtyGetBaud(sgttyb.sg_ospeed);
  958.     parity = 'n';
  959.     if (sgttyb.sg_flags & EVENP) {
  960.     parity = 'e';
  961.     } else if (sgttyb.sg_flags & ODDP) {
  962.     parity = 'o';
  963.     }
  964.     ttyPtr->parity = parity;
  965.     ttyPtr->data = (sgttyb.sg_flags & (EVENP | ODDP)) ? 7 : 8;
  966.     ttyPtr->stop = 1;
  967. #else    /* !USE_SGTTY */
  968.     ttyPtr->baud = 0;
  969.     ttyPtr->parity = 'n';
  970.     ttyPtr->data = 0;
  971.     ttyPtr->stop = 0;
  972. #endif    /* !USE_SGTTY */
  973. #endif    /* !USE_TERMIO */
  974. #endif    /* !USE_TERMIOS */
  975. }
  976.  
  977. /*
  978.  *---------------------------------------------------------------------------
  979.  *
  980.  * TtySetAttributes --
  981.  *
  982.  *    Set the current attributes of the specified serial device. 
  983.  *
  984.  * Results:
  985.  *    None.
  986.  *
  987.  * Side effects:
  988.  *    None.
  989.  *
  990.  *---------------------------------------------------------------------------
  991.  */
  992.  
  993. static void
  994. TtySetAttributes(fd, ttyPtr)
  995.     int fd;            /* Open file descriptor for serial port to
  996.                  * be modified. */
  997.     TtyAttrs *ttyPtr;        /* Buffer containing new attributes for
  998.                  * serial port. */
  999. {
  1000. #ifdef USE_TERMIOS
  1001.     int parity, data, flag;
  1002.     struct termios termios;
  1003.  
  1004.     tcgetattr(fd, &termios);
  1005.     cfsetospeed(&termios, TtyGetSpeed(ttyPtr->baud));
  1006.     cfsetispeed(&termios, TtyGetSpeed(ttyPtr->baud));
  1007.  
  1008.     flag = 0;
  1009.     parity = ttyPtr->parity;
  1010.     if (parity != 'n') {
  1011.     flag |= PARENB;
  1012. #ifdef PAREXT
  1013.     termios.c_cflag &= ~PAREXT;
  1014.     if ((parity == 'm') || (parity == 's')) {
  1015.         flag |= PAREXT;
  1016.     }
  1017. #endif
  1018.     if ((parity == 'm') || (parity == 'o')) {
  1019.         flag |= PARODD;
  1020.     }
  1021.     }
  1022.     data = ttyPtr->data;
  1023.     flag |= (data == 5) ? CS5 : (data == 6) ? CS6 : (data == 7) ? CS7 : CS8;
  1024.     if (ttyPtr->stop == 2) {
  1025.     flag |= CSTOPB;
  1026.     }
  1027.  
  1028.     termios.c_cflag &= ~(PARENB | PARODD | CSIZE | CSTOPB);
  1029.     termios.c_cflag |= flag;
  1030.     tcsetattr(fd, TCSANOW, &termios);
  1031.  
  1032. #else    /* !USE_TERMIOS */
  1033. #ifdef USE_TERMIO
  1034.     int parity, data, flag;
  1035.     struct termio termio;
  1036.  
  1037.     ioctl(fd, TCGETA, &termio);
  1038.     termio.c_cflag &= ~CBAUD;
  1039.     termio.c_cflag |= TtyGetSpeed(ttyPtr->baud);
  1040.  
  1041.     flag = 0;
  1042.     parity = ttyPtr->parity;
  1043.     if (parity != 'n') {
  1044.     flag |= PARENB;
  1045.     if ((parity == 'm') || (parity == 's')) {
  1046.         flag |= PAREXT;
  1047.     }
  1048.     if ((parity == 'm') || (parity == 'o')) {
  1049.         flag |= PARODD;
  1050.     }
  1051.     }
  1052.     data = ttyPtr->data;
  1053.     flag |= (data == 5) ? CS5 : (data == 6) ? CS6 : (data == 7) ? CS7 : CS8;
  1054.     if (ttyPtr->stop == 2) {
  1055.     flag |= CSTOPB;
  1056.     }
  1057.  
  1058.     termio.c_cflag &= ~(PARENB | PARODD | PAREXT | CSIZE | CSTOPB);
  1059.     termio.c_cflag |= flag;
  1060.     ioctl(fd, TCSETAW, &termio);
  1061.  
  1062. #else    /* !USE_TERMIO */
  1063. #ifdef USE_SGTTY
  1064.     int parity;
  1065.     struct sgttyb sgttyb;
  1066.  
  1067.     ioctl(fd, TIOCGETP, &sgttyb);
  1068.     sgttyb.sg_ospeed = TtyGetSpeed(ttyPtr->baud);
  1069.     sgttyb.sg_ispeed = TtyGetSpeed(ttyPtr->baud);
  1070.  
  1071.     parity = ttyPtr->parity;
  1072.     if (parity == 'e') {
  1073.     sgttyb.sg_flags &= ~ODDP;
  1074.     sgttyb.sg_flags |= EVENP;
  1075.     } else if (parity == 'o') {
  1076.     sgttyb.sg_flags &= ~EVENP;
  1077.     sgttyb.sg_flags |= ODDP;
  1078.     }
  1079.     ioctl(fd, TIOCSETP, &sgttyb);
  1080. #endif    /* USE_SGTTY */
  1081. #endif    /* !USE_TERMIO */
  1082. #endif    /* !USE_TERMIOS */
  1083. }
  1084.  
  1085. /*
  1086.  *---------------------------------------------------------------------------
  1087.  *
  1088.  * TtyParseMode --
  1089.  *
  1090.  *    Parse the "-mode" argument to the fconfigure command.  The argument
  1091.  *    is of the form baud,parity,data,stop.
  1092.  *
  1093.  * Results:
  1094.  *    The return value is TCL_OK if the argument was successfully
  1095.  *    parsed, TCL_ERROR otherwise.  If TCL_ERROR is returned, an
  1096.  *    error message is left in interp->result (if interp is non-NULL).
  1097.  *
  1098.  * Side effects:
  1099.  *    None.
  1100.  *
  1101.  *---------------------------------------------------------------------------
  1102.  */
  1103.  
  1104. static int
  1105. TtyParseMode(interp, mode, speedPtr, parityPtr, dataPtr, stopPtr)
  1106.     Tcl_Interp *interp;        /* If non-NULL, interp for error return. */
  1107.     CONST char *mode;        /* Mode string to be parsed. */
  1108.     int *speedPtr;        /* Filled with baud rate from mode string. */
  1109.     int *parityPtr;        /* Filled with parity from mode string. */
  1110.     int *dataPtr;        /* Filled with data bits from mode string. */
  1111.     int *stopPtr;        /* Filled with stop bits from mode string. */
  1112. {
  1113.     int i, end;
  1114.     char parity;
  1115.     static char *bad = "bad value for -mode";
  1116.  
  1117.     i = sscanf(mode, "%d,%c,%d,%d%n", speedPtr, &parity, dataPtr,
  1118.         stopPtr, &end);
  1119.     if ((i != 4) || (mode[end] != '\0')) {
  1120.     if (interp != NULL) {
  1121.         Tcl_AppendResult(interp, bad, ": should be baud,parity,data,stop",
  1122.             NULL);
  1123.     }
  1124.     return TCL_ERROR;
  1125.     }
  1126.     if (strchr("noems", parity) == NULL) {
  1127.     if (interp != NULL) {
  1128.         Tcl_AppendResult(interp, bad,
  1129.             " parity: should be n, o, e, m, or s", NULL);
  1130.     }
  1131.     return TCL_ERROR;
  1132.     }
  1133.     *parityPtr = parity;
  1134.     if ((*dataPtr < 5) || (*dataPtr > 8)) {
  1135.     if (interp != NULL) {
  1136.         Tcl_AppendResult(interp, bad, " data: should be 5, 6, 7, or 8",
  1137.             NULL);
  1138.     }
  1139.     return TCL_ERROR;
  1140.     }
  1141.     if ((*stopPtr < 0) || (*stopPtr > 2)) {
  1142.     if (interp != NULL) {
  1143.         Tcl_AppendResult(interp, bad, " stop: should be 1 or 2", NULL);
  1144.     }
  1145.     return TCL_ERROR;
  1146.     }
  1147.     return TCL_OK;
  1148. }
  1149.  
  1150. /*
  1151.  *----------------------------------------------------------------------
  1152.  *
  1153.  * Tcl_OpenFileChannel --
  1154.  *
  1155.  *    Open an file based channel on Unix systems.
  1156.  *
  1157.  * Results:
  1158.  *    The new channel or NULL. If NULL, the output argument
  1159.  *    errorCodePtr is set to a POSIX error and an error message is
  1160.  *    left in interp->result if interp is not NULL.
  1161.  *
  1162.  * Side effects:
  1163.  *    May open the channel and may cause creation of a file on the
  1164.  *    file system.
  1165.  *
  1166.  *----------------------------------------------------------------------
  1167.  */
  1168.  
  1169. Tcl_Channel
  1170. Tcl_OpenFileChannel(interp, fileName, modeString, permissions)
  1171.     Tcl_Interp *interp;            /* Interpreter for error reporting;
  1172.                                          * can be NULL. */
  1173.     char *fileName;            /* Name of file to open. */
  1174.     char *modeString;            /* A list of POSIX open modes or
  1175.                                          * a string such as "rw". */
  1176.     int permissions;            /* If the open involves creating a
  1177.                                          * file, with what modes to create
  1178.                                          * it? */
  1179. {
  1180.     int fd, seekFlag, mode, channelPermissions;
  1181.     FileState *fsPtr;
  1182.     char *nativeName, channelName[20];
  1183.     Tcl_DString buffer;
  1184.     Tcl_ChannelType *channelTypePtr;
  1185.  
  1186.     mode = TclGetOpenMode(interp, modeString, &seekFlag);
  1187.     if (mode == -1) {
  1188.         return NULL;
  1189.     }
  1190.     switch (mode & (O_RDONLY | O_WRONLY | O_RDWR)) {
  1191.     case O_RDONLY:
  1192.         channelPermissions = TCL_READABLE;
  1193.         break;
  1194.     case O_WRONLY:
  1195.         channelPermissions = TCL_WRITABLE;
  1196.         break;
  1197.     case O_RDWR:
  1198.         channelPermissions = (TCL_READABLE | TCL_WRITABLE);
  1199.         break;
  1200.     default:
  1201.             /*
  1202.              * This may occurr if modeString was "", for example.
  1203.              */
  1204.         panic("Tcl_OpenFileChannel: invalid mode value");
  1205.         return NULL;
  1206.     }
  1207.  
  1208.     nativeName = Tcl_TranslateFileName(interp, fileName, &buffer);
  1209.     if (nativeName == NULL) {
  1210.     return NULL;
  1211.     }
  1212.     fd = open(nativeName, mode, permissions);
  1213.  
  1214.     /*
  1215.      * If nativeName is not NULL, the buffer is valid and we must free
  1216.      * the storage.
  1217.      */
  1218.     
  1219.     Tcl_DStringFree(&buffer);
  1220.  
  1221.     if (fd < 0) {
  1222.         if (interp != (Tcl_Interp *) NULL) {
  1223.             Tcl_AppendResult(interp, "couldn't open \"", fileName, "\": ",
  1224.                     Tcl_PosixError(interp), (char *) NULL);
  1225.         }
  1226.         return NULL;
  1227.     }
  1228.  
  1229.     /*
  1230.      * Set close-on-exec flag on the fd so that child processes will not
  1231.      * inherit this fd.
  1232.      */
  1233.   
  1234.     fcntl(fd, F_SETFD, FD_CLOEXEC);
  1235.     
  1236.     sprintf(channelName, "file%d", fd);
  1237.     
  1238.     fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
  1239.     fsPtr->nextPtr = firstFilePtr;
  1240.     firstFilePtr = fsPtr;
  1241.     fsPtr->validMask = channelPermissions | TCL_EXCEPTION;
  1242.     fsPtr->fd = fd;
  1243.     
  1244.     if (isatty(fd)) {
  1245.     /*
  1246.      * Initialize the serial port to a set of sane parameters.
  1247.      * Especially important if the remote device is set to echo and
  1248.      * the serial port driver was also set to echo -- as soon as a char
  1249.      * were sent to the serial port, the remote device would echo it,
  1250.      * then the serial driver would echo it back to the device, etc.
  1251.      */
  1252.      
  1253.     TtyInit(fd);
  1254.     channelTypePtr = &ttyChannelType;
  1255.     } else {
  1256.     channelTypePtr = &fileChannelType;
  1257.     }
  1258.  
  1259.     fsPtr->channel = Tcl_CreateChannel(channelTypePtr, channelName,
  1260.         (ClientData) fsPtr, channelPermissions);
  1261.  
  1262.     if (seekFlag) {
  1263.         if (Tcl_Seek(fsPtr->channel, 0, SEEK_END) < 0) {
  1264.             if (interp != (Tcl_Interp *) NULL) {
  1265.                 Tcl_AppendResult(interp, "couldn't seek to end of file on \"",
  1266.                         channelName, "\": ", Tcl_PosixError(interp), NULL);
  1267.             }
  1268.             Tcl_Close(NULL, fsPtr->channel);
  1269.             return NULL;
  1270.         }
  1271.     }
  1272.  
  1273.     if (channelTypePtr == &ttyChannelType) {
  1274.     /*
  1275.      * Gotcha.  Most modems need a "\r" at the end of the command
  1276.      * sequence.  If you just send "at\n", the modem will not respond
  1277.      * with "OK" because it never got a "\r" to actually invoke the
  1278.      * command.  So, by default, newlines are translated to "\r\n" on
  1279.      * output to avoid "bug" reports that the serial port isn't working.
  1280.      */
  1281.      
  1282.     if (Tcl_SetChannelOption(interp, fsPtr->channel, "-translation",
  1283.         "auto crlf") != TCL_OK) {
  1284.         Tcl_Close(NULL, fsPtr->channel);
  1285.         return NULL;
  1286.     }
  1287.     }
  1288.  
  1289.     return fsPtr->channel;
  1290. }
  1291.  
  1292. /*
  1293.  *----------------------------------------------------------------------
  1294.  *
  1295.  * Tcl_MakeFileChannel --
  1296.  *
  1297.  *    Makes a Tcl_Channel from an existing OS level file handle.
  1298.  *
  1299.  * Results:
  1300.  *    The Tcl_Channel created around the preexisting OS level file handle.
  1301.  *
  1302.  * Side effects:
  1303.  *    None.
  1304.  *
  1305.  *----------------------------------------------------------------------
  1306.  */
  1307.  
  1308. Tcl_Channel
  1309. Tcl_MakeFileChannel(handle, mode)
  1310.     ClientData handle;        /* OS level handle. */
  1311.     int mode;            /* ORed combination of TCL_READABLE and
  1312.                                  * TCL_WRITABLE to indicate file mode. */
  1313. {
  1314.     FileState *fsPtr;
  1315.     char channelName[20];
  1316.     int fd = (int) handle;
  1317.  
  1318.     if (mode == 0) {
  1319.         return NULL;
  1320.     }
  1321.  
  1322.     sprintf(channelName, "file%d", fd);
  1323.  
  1324.     /*
  1325.      * Look to see if a channel with this fd and the same mode already exists.
  1326.      * If the fd is used, but the mode doesn't match, return NULL.
  1327.      */
  1328.     
  1329.     for (fsPtr = firstFilePtr; fsPtr != NULL; fsPtr = fsPtr->nextPtr) {
  1330.     if (fsPtr->fd == fd) {
  1331.         return (mode == fsPtr->validMask) ? fsPtr->channel : NULL;
  1332.     }
  1333.     }
  1334.  
  1335.     fsPtr = (FileState *) ckalloc((unsigned) sizeof(FileState));
  1336.     fsPtr->nextPtr = firstFilePtr;
  1337.     firstFilePtr = fsPtr;
  1338.     fsPtr->fd = fd;
  1339.     fsPtr->validMask = mode | TCL_EXCEPTION;
  1340.     fsPtr->channel = Tcl_CreateChannel(&fileChannelType, channelName,
  1341.             (ClientData) fsPtr, mode);
  1342.     
  1343.     return fsPtr->channel;
  1344. }
  1345.  
  1346. /*
  1347.  *----------------------------------------------------------------------
  1348.  *
  1349.  * TcpBlockModeProc --
  1350.  *
  1351.  *    This procedure is invoked by the generic IO level to set blocking
  1352.  *    and nonblocking mode on a TCP socket based channel.
  1353.  *
  1354.  * Results:
  1355.  *    0 if successful, errno when failed.
  1356.  *
  1357.  * Side effects:
  1358.  *    Sets the device into blocking or nonblocking mode.
  1359.  *
  1360.  *----------------------------------------------------------------------
  1361.  */
  1362.  
  1363.     /* ARGSUSED */
  1364. static int
  1365. TcpBlockModeProc(instanceData, mode)
  1366.     ClientData instanceData;        /* Socket state. */
  1367.     int mode;                /* The mode to set. Can be one of
  1368.                                          * TCL_MODE_BLOCKING or
  1369.                                          * TCL_MODE_NONBLOCKING. */
  1370. {
  1371.     TcpState *statePtr = (TcpState *) instanceData;
  1372.     int setting;
  1373.     
  1374. #ifndef    USE_FIONBIO
  1375.     setting = fcntl(statePtr->fd, F_GETFL);
  1376.     if (mode == TCL_MODE_BLOCKING) {
  1377.         statePtr->flags &= (~(TCP_ASYNC_SOCKET));
  1378.         setting &= (~(O_NONBLOCK));
  1379.     } else {
  1380.         statePtr->flags |= TCP_ASYNC_SOCKET;
  1381.         setting |= O_NONBLOCK;
  1382.     }
  1383.     if (fcntl(statePtr->fd, F_SETFL, setting) < 0) {
  1384.         return errno;
  1385.     }
  1386. #endif
  1387.  
  1388. #ifdef    USE_FIONBIO
  1389.     if (mode == TCL_MODE_BLOCKING) {
  1390.         statePtr->flags &= (~(TCP_ASYNC_SOCKET));
  1391.         setting = 0;
  1392.         if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
  1393.             return errno;
  1394.         }
  1395.     } else {
  1396.         statePtr->flags |= TCP_ASYNC_SOCKET;
  1397.         setting = 1;
  1398.         if (ioctl(statePtr->fd, (int) FIONBIO, &setting) == -1) {
  1399.             return errno;
  1400.         }
  1401.     }
  1402. #endif
  1403.  
  1404.     return 0;
  1405. }
  1406.  
  1407. /*
  1408.  *----------------------------------------------------------------------
  1409.  *
  1410.  * WaitForConnect --
  1411.  *
  1412.  *    Waits for a connection on an asynchronously opened socket to
  1413.  *    be completed.
  1414.  *
  1415.  * Results:
  1416.  *    None.
  1417.  *
  1418.  * Side effects:
  1419.  *    The socket is connected after this function returns.
  1420.  *
  1421.  *----------------------------------------------------------------------
  1422.  */
  1423.  
  1424. static int
  1425. WaitForConnect(statePtr, errorCodePtr)
  1426.     TcpState *statePtr;        /* State of the socket. */
  1427.     int *errorCodePtr;        /* Where to store errors? */
  1428. {
  1429.     int timeOut;        /* How long to wait. */
  1430.     int state;            /* Of calling TclWaitForFile. */
  1431.     int flags;            /* fcntl flags for the socket. */
  1432.  
  1433.     /*
  1434.      * If an asynchronous connect is in progress, attempt to wait for it
  1435.      * to complete before reading.
  1436.      */
  1437.     
  1438.     if (statePtr->flags & TCP_ASYNC_CONNECT) {
  1439.         if (statePtr->flags & TCP_ASYNC_SOCKET) {
  1440.             timeOut = 0;
  1441.         } else {
  1442.             timeOut = -1;
  1443.         }
  1444.         errno = 0;
  1445.         state = TclUnixWaitForFile(statePtr->fd,
  1446.         TCL_WRITABLE | TCL_EXCEPTION, timeOut);
  1447.         if (!(statePtr->flags & TCP_ASYNC_SOCKET)) {
  1448. #ifndef    USE_FIONBIO
  1449.             flags = fcntl(statePtr->fd, F_GETFL);
  1450.             flags &= (~(O_NONBLOCK));
  1451.             (void) fcntl(statePtr->fd, F_SETFL, flags);
  1452. #endif
  1453.  
  1454. #ifdef    USE_FIONBIO
  1455.             flags = 0;
  1456.             (void) ioctl(statePtr->fd, FIONBIO, &flags);
  1457. #endif
  1458.         }
  1459.         if (state & TCL_EXCEPTION) {
  1460.             return -1;
  1461.         }
  1462.         if (state & TCL_WRITABLE) {
  1463.             statePtr->flags &= (~(TCP_ASYNC_CONNECT));
  1464.         } else if (timeOut == 0) {
  1465.             *errorCodePtr = errno = EWOULDBLOCK;
  1466.             return -1;
  1467.         }
  1468.     }
  1469.     return 0;
  1470. }
  1471.  
  1472. /*
  1473.  *----------------------------------------------------------------------
  1474.  *
  1475.  * TcpInputProc --
  1476.  *
  1477.  *    This procedure is invoked by the generic IO level to read input
  1478.  *    from a TCP socket based channel.
  1479.  *
  1480.  *    NOTE: We cannot share code with FilePipeInputProc because here
  1481.  *    we must use recv to obtain the input from the channel, not read.
  1482.  *
  1483.  * Results:
  1484.  *    The number of bytes read is returned or -1 on error. An output
  1485.  *    argument contains the POSIX error code on error, or zero if no
  1486.  *    error occurred.
  1487.  *
  1488.  * Side effects:
  1489.  *    Reads input from the input device of the channel.
  1490.  *
  1491.  *----------------------------------------------------------------------
  1492.  */
  1493.  
  1494.     /* ARGSUSED */
  1495. static int
  1496. TcpInputProc(instanceData, buf, bufSize, errorCodePtr)
  1497.     ClientData instanceData;        /* Socket state. */
  1498.     char *buf;                /* Where to store data read. */
  1499.     int bufSize;            /* How much space is available
  1500.                                          * in the buffer? */
  1501.     int *errorCodePtr;            /* Where to store error code. */
  1502. {
  1503.     TcpState *statePtr = (TcpState *) instanceData;
  1504.     int bytesRead, state;
  1505.  
  1506.     *errorCodePtr = 0;
  1507.     state = WaitForConnect(statePtr, errorCodePtr);
  1508.     if (state != 0) {
  1509.         return -1;
  1510.     }
  1511.     bytesRead = recv(statePtr->fd, buf, bufSize, 0);
  1512.     if (bytesRead > -1) {
  1513.         return bytesRead;
  1514.     }
  1515.     if (errno == ECONNRESET) {
  1516.  
  1517.         /*
  1518.          * Turn ECONNRESET into a soft EOF condition.
  1519.          */
  1520.         
  1521.         return 0;
  1522.     }
  1523.     *errorCodePtr = errno;
  1524.     return -1;
  1525. }
  1526.  
  1527. /*
  1528.  *----------------------------------------------------------------------
  1529.  *
  1530.  * TcpOutputProc --
  1531.  *
  1532.  *    This procedure is invoked by the generic IO level to write output
  1533.  *    to a TCP socket based channel.
  1534.  *
  1535.  *    NOTE: We cannot share code with FilePipeOutputProc because here
  1536.  *    we must use send, not write, to get reliable error reporting.
  1537.  *
  1538.  * Results:
  1539.  *    The number of bytes written is returned. An output argument is
  1540.  *    set to a POSIX error code if an error occurred, or zero.
  1541.  *
  1542.  * Side effects:
  1543.  *    Writes output on the output device of the channel.
  1544.  *
  1545.  *----------------------------------------------------------------------
  1546.  */
  1547.  
  1548. static int
  1549. TcpOutputProc(instanceData, buf, toWrite, errorCodePtr)
  1550.     ClientData instanceData;        /* Socket state. */
  1551.     char *buf;                /* The data buffer. */
  1552.     int toWrite;            /* How many bytes to write? */
  1553.     int *errorCodePtr;            /* Where to store error code. */
  1554. {
  1555.     TcpState *statePtr = (TcpState *) instanceData;
  1556.     int written;
  1557.     int state;                /* Of waiting for connection. */
  1558.  
  1559.     *errorCodePtr = 0;
  1560.     state = WaitForConnect(statePtr, errorCodePtr);
  1561.     if (state != 0) {
  1562.         return -1;
  1563.     }
  1564.     written = send(statePtr->fd, buf, toWrite, 0);
  1565.     if (written > -1) {
  1566.         return written;
  1567.     }
  1568.     *errorCodePtr = errno;
  1569.     return -1;
  1570. }
  1571.  
  1572. /*
  1573.  *----------------------------------------------------------------------
  1574.  *
  1575.  * TcpCloseProc --
  1576.  *
  1577.  *    This procedure is invoked by the generic IO level to perform
  1578.  *    channel-type-specific cleanup when a TCP socket based channel
  1579.  *    is closed.
  1580.  *
  1581.  * Results:
  1582.  *    0 if successful, the value of errno if failed.
  1583.  *
  1584.  * Side effects:
  1585.  *    Closes the socket of the channel.
  1586.  *
  1587.  *----------------------------------------------------------------------
  1588.  */
  1589.  
  1590.     /* ARGSUSED */
  1591. static int
  1592. TcpCloseProc(instanceData, interp)
  1593.     ClientData instanceData;    /* The socket to close. */
  1594.     Tcl_Interp *interp;        /* For error reporting - unused. */
  1595. {
  1596.     TcpState *statePtr = (TcpState *) instanceData;
  1597.     int errorCode = 0;
  1598.  
  1599.     /*
  1600.      * Delete a file handler that may be active for this socket if this
  1601.      * is a server socket - the file handler was created automatically
  1602.      * by Tcl as part of the mechanism to accept new client connections.
  1603.      * Channel handlers are already deleted in the generic IO channel
  1604.      * closing code that called this function, so we do not have to
  1605.      * delete them here.
  1606.      */
  1607.     
  1608.     Tcl_DeleteFileHandler(statePtr->fd);
  1609.  
  1610.     if (close(statePtr->fd) < 0) {
  1611.     errorCode = errno;
  1612.     }
  1613.     ckfree((char *) statePtr);
  1614.  
  1615.     return errorCode;
  1616. }
  1617.  
  1618. /*
  1619.  *----------------------------------------------------------------------
  1620.  *
  1621.  * TcpGetOptionProc --
  1622.  *
  1623.  *    Computes an option value for a TCP socket based channel, or a
  1624.  *    list of all options and their values.
  1625.  *
  1626.  *    Note: This code is based on code contributed by John Haxby.
  1627.  *
  1628.  * Results:
  1629.  *    A standard Tcl result. The value of the specified option or a
  1630.  *    list of all options and    their values is returned in the
  1631.  *    supplied DString. Sets Error message if needed.
  1632.  *
  1633.  * Side effects:
  1634.  *    None.
  1635.  *
  1636.  *----------------------------------------------------------------------
  1637.  */
  1638.  
  1639. static int
  1640. TcpGetOptionProc(instanceData, interp, optionName, dsPtr)
  1641.     ClientData instanceData;     /* Socket state. */
  1642.     Tcl_Interp *interp;          /* For error reporting - can be NULL. */
  1643.     char *optionName;             /* Name of the option to
  1644.                   * retrieve the value for, or
  1645.                   * NULL to get all options and
  1646.                   * their values. */
  1647.     Tcl_DString *dsPtr;             /* Where to store the computed
  1648.                   * value; initialized by caller. */
  1649. {
  1650.     TcpState *statePtr = (TcpState *) instanceData;
  1651.     struct sockaddr_in sockname;
  1652.     struct sockaddr_in peername;
  1653.     struct hostent *hostEntPtr;
  1654.     int size = sizeof(struct sockaddr_in);
  1655.     size_t len = 0;
  1656.     char buf[128];
  1657.  
  1658.     if (optionName != (char *) NULL) {
  1659.         len = strlen(optionName);
  1660.     }
  1661.  
  1662.     if ((len == 0) ||
  1663.             ((len > 1) && (optionName[1] == 'p') &&
  1664.                     (strncmp(optionName, "-peername", len) == 0))) {
  1665.         if (getpeername(statePtr->fd, (struct sockaddr *) &peername, &size)
  1666.         >= 0) {
  1667.             if (len == 0) {
  1668.                 Tcl_DStringAppendElement(dsPtr, "-peername");
  1669.                 Tcl_DStringStartSublist(dsPtr);
  1670.             }
  1671.             Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
  1672.             hostEntPtr = gethostbyaddr((char *) &(peername.sin_addr),
  1673.                     sizeof(peername.sin_addr), AF_INET);
  1674.             if (hostEntPtr != (struct hostent *) NULL) {
  1675.                 Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
  1676.             } else {
  1677.                 Tcl_DStringAppendElement(dsPtr, inet_ntoa(peername.sin_addr));
  1678.             }
  1679.             sprintf(buf, "%d", ntohs(peername.sin_port));
  1680.             Tcl_DStringAppendElement(dsPtr, buf);
  1681.             if (len == 0) {
  1682.                 Tcl_DStringEndSublist(dsPtr);
  1683.             } else {
  1684.                 return TCL_OK;
  1685.             }
  1686.         } else {
  1687.             /*
  1688.              * getpeername failed - but if we were asked for all the options
  1689.              * (len==0), don't flag an error at that point because it could
  1690.              * be an fconfigure request on a server socket. (which have
  1691.              * no peer). same must be done on win&mac.
  1692.              */
  1693.  
  1694.             if (len) {
  1695.                 if (interp) {
  1696.                     Tcl_AppendResult(interp, "can't get peername: ",
  1697.                                      Tcl_PosixError(interp),
  1698.                                      (char *) NULL);
  1699.                 }
  1700.                 return TCL_ERROR;
  1701.             }
  1702.         }
  1703.     }
  1704.  
  1705.     if ((len == 0) ||
  1706.             ((len > 1) && (optionName[1] == 's') &&
  1707.                     (strncmp(optionName, "-sockname", len) == 0))) {
  1708.         if (getsockname(statePtr->fd, (struct sockaddr *) &sockname, &size)
  1709.         >= 0) {
  1710.             if (len == 0) {
  1711.                 Tcl_DStringAppendElement(dsPtr, "-sockname");
  1712.                 Tcl_DStringStartSublist(dsPtr);
  1713.             }
  1714.             Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
  1715.             hostEntPtr = gethostbyaddr((char *) &(sockname.sin_addr),
  1716.                     sizeof(peername.sin_addr), AF_INET);
  1717.             if (hostEntPtr != (struct hostent *) NULL) {
  1718.                 Tcl_DStringAppendElement(dsPtr, hostEntPtr->h_name);
  1719.             } else {
  1720.                 Tcl_DStringAppendElement(dsPtr, inet_ntoa(sockname.sin_addr));
  1721.             }
  1722.             sprintf(buf, "%d", ntohs(sockname.sin_port));
  1723.             Tcl_DStringAppendElement(dsPtr, buf);
  1724.             if (len == 0) {
  1725.                 Tcl_DStringEndSublist(dsPtr);
  1726.             } else {
  1727.                 return TCL_OK;
  1728.             }
  1729.         } else {
  1730.         if (interp) {
  1731.         Tcl_AppendResult(interp, "can't get sockname: ",
  1732.                  Tcl_PosixError(interp),
  1733.                  (char *) NULL);
  1734.         }
  1735.         return TCL_ERROR;
  1736.     }
  1737.     }
  1738.  
  1739.     if (len > 0) {
  1740.         return Tcl_BadChannelOption(interp, optionName, "peername sockname");
  1741.     }
  1742.  
  1743.     return TCL_OK;
  1744. }
  1745.  
  1746. /*
  1747.  *----------------------------------------------------------------------
  1748.  *
  1749.  * TcpWatchProc --
  1750.  *
  1751.  *    Initialize the notifier to watch the fd from this channel.
  1752.  *
  1753.  * Results:
  1754.  *    None.
  1755.  *
  1756.  * Side effects:
  1757.  *    Sets up the notifier so that a future event on the channel will
  1758.  *    be seen by Tcl.
  1759.  *
  1760.  *----------------------------------------------------------------------
  1761.  */
  1762.  
  1763. static void
  1764. TcpWatchProc(instanceData, mask)
  1765.     ClientData instanceData;        /* The socket state. */
  1766.     int mask;                /* Events of interest; an OR-ed
  1767.                                          * combination of TCL_READABLE,
  1768.                                          * TCL_WRITABLE and TCL_EXCEPTION. */
  1769. {
  1770.     TcpState *statePtr = (TcpState *) instanceData;
  1771.  
  1772.     if (mask) {
  1773.     Tcl_CreateFileHandler(statePtr->fd, mask,
  1774.         (Tcl_FileProc *) Tcl_NotifyChannel,
  1775.         (ClientData) statePtr->channel);
  1776.     } else {
  1777.     Tcl_DeleteFileHandler(statePtr->fd);
  1778.     }
  1779. }
  1780.  
  1781. /*
  1782.  *----------------------------------------------------------------------
  1783.  *
  1784.  * TcpGetHandleProc --
  1785.  *
  1786.  *    Called from Tcl_GetChannelFile to retrieve OS handles from inside
  1787.  *    a TCP socket based channel.
  1788.  *
  1789.  * Results:
  1790.  *    Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
  1791.  *    there is no handle for the specified direction. 
  1792.  *
  1793.  * Side effects:
  1794.  *    None.
  1795.  *
  1796.  *----------------------------------------------------------------------
  1797.  */
  1798.  
  1799.     /* ARGSUSED */
  1800. static int
  1801. TcpGetHandleProc(instanceData, direction, handlePtr)
  1802.     ClientData instanceData;    /* The socket state. */
  1803.     int direction;        /* Not used. */
  1804.     ClientData *handlePtr;    /* Where to store the handle.  */
  1805. {
  1806.     TcpState *statePtr = (TcpState *) instanceData;
  1807.  
  1808.     *handlePtr = (ClientData)statePtr->fd;
  1809.     return TCL_OK;
  1810. }
  1811.  
  1812. /*
  1813.  *----------------------------------------------------------------------
  1814.  *
  1815.  * CreateSocket --
  1816.  *
  1817.  *    This function opens a new socket in client or server mode
  1818.  *    and initializes the TcpState structure.
  1819.  *
  1820.  * Results:
  1821.  *    Returns a new TcpState, or NULL with an error in interp->result,
  1822.  *    if interp is not NULL.
  1823.  *
  1824.  * Side effects:
  1825.  *    Opens a socket.
  1826.  *
  1827.  *----------------------------------------------------------------------
  1828.  */
  1829.  
  1830. static TcpState *
  1831. CreateSocket(interp, port, host, server, myaddr, myport, async)
  1832.     Tcl_Interp *interp;        /* For error reporting; can be NULL. */
  1833.     int port;            /* Port number to open. */
  1834.     char *host;            /* Name of host on which to open port.
  1835.                  * NULL implies INADDR_ANY */
  1836.     int server;            /* 1 if socket should be a server socket,
  1837.                  * else 0 for a client socket. */
  1838.     char *myaddr;        /* Optional client-side address */
  1839.     int myport;            /* Optional client-side port */
  1840.     int async;            /* If nonzero and creating a client socket,
  1841.                                  * attempt to do an async connect. Otherwise
  1842.                                  * do a synchronous connect or bind. */
  1843. {
  1844.     int status, sock, asyncConnect, curState, origState;
  1845.     struct sockaddr_in sockaddr;    /* socket address */
  1846.     struct sockaddr_in mysockaddr;    /* Socket address for client */
  1847.     TcpState *statePtr;
  1848.  
  1849.     sock = -1;
  1850.     origState = 0;
  1851.     if (! CreateSocketAddress(&sockaddr, host, port)) {
  1852.     goto addressError;
  1853.     }
  1854.     if ((myaddr != NULL || myport != 0) &&
  1855.         ! CreateSocketAddress(&mysockaddr, myaddr, myport)) {
  1856.     goto addressError;
  1857.     }
  1858.  
  1859.     sock = socket(AF_INET, SOCK_STREAM, 0);
  1860.     if (sock < 0) {
  1861.     goto addressError;
  1862.     }
  1863.  
  1864.     /*
  1865.      * Set the close-on-exec flag so that the socket will not get
  1866.      * inherited by child processes.
  1867.      */
  1868.  
  1869.     fcntl(sock, F_SETFD, FD_CLOEXEC);
  1870.     
  1871.     /*
  1872.      * Set kernel space buffering
  1873.      */
  1874.  
  1875.     TclSockMinimumBuffers(sock, SOCKET_BUFSIZE);
  1876.  
  1877.     asyncConnect = 0;
  1878.     status = 0;
  1879.     if (server) {
  1880.  
  1881.     /*
  1882.      * Set up to reuse server addresses automatically and bind to the
  1883.      * specified port.
  1884.      */
  1885.     
  1886.     status = 1;
  1887.     (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &status,
  1888.         sizeof(status));
  1889.     status = bind(sock, (struct sockaddr *) &sockaddr,
  1890.                 sizeof(struct sockaddr));
  1891.     if (status != -1) {
  1892.         status = listen(sock, SOMAXCONN);
  1893.     } 
  1894.     } else {
  1895.     if (myaddr != NULL || myport != 0) { 
  1896.         curState = 1;
  1897.         (void) setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
  1898.                     (char *) &curState, sizeof(curState));
  1899.         status = bind(sock, (struct sockaddr *) &mysockaddr,
  1900.             sizeof(struct sockaddr));
  1901.         if (status < 0) {
  1902.         goto bindError;
  1903.         }
  1904.     }
  1905.  
  1906.     /*
  1907.      * Attempt to connect. The connect may fail at present with an
  1908.      * EINPROGRESS but at a later time it will complete. The caller
  1909.      * will set up a file handler on the socket if she is interested in
  1910.      * being informed when the connect completes.
  1911.      */
  1912.  
  1913.         if (async) {
  1914. #ifndef    USE_FIONBIO
  1915.             origState = fcntl(sock, F_GETFL);
  1916.             curState = origState | O_NONBLOCK;
  1917.             status = fcntl(sock, F_SETFL, curState);
  1918. #endif
  1919.  
  1920. #ifdef    USE_FIONBIO
  1921.             curState = 1;
  1922.             status = ioctl(sock, FIONBIO, &curState);
  1923. #endif            
  1924.         } else {
  1925.             status = 0;
  1926.         }
  1927.         if (status > -1) {
  1928.             status = connect(sock, (struct sockaddr *) &sockaddr,
  1929.                     sizeof(sockaddr));
  1930.             if (status < 0) {
  1931.                 if (errno == EINPROGRESS) {
  1932.                     asyncConnect = 1;
  1933.                     status = 0;
  1934.                 }
  1935.             }
  1936.         }
  1937.     }
  1938.  
  1939. bindError:
  1940.     if (status < 0) {
  1941.         if (interp != NULL) {
  1942.             Tcl_AppendResult(interp, "couldn't open socket: ",
  1943.                     Tcl_PosixError(interp), (char *) NULL);
  1944.         }
  1945.         if (sock != -1) {
  1946.             close(sock);
  1947.         }
  1948.         return NULL;
  1949.     }
  1950.  
  1951.     /*
  1952.      * Allocate a new TcpState for this socket.
  1953.      */
  1954.  
  1955.     statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
  1956.     statePtr->flags = 0;
  1957.     if (asyncConnect) {
  1958.         statePtr->flags = TCP_ASYNC_CONNECT;
  1959.     }
  1960.     statePtr->fd = sock;
  1961.     
  1962.     return statePtr;
  1963.  
  1964. addressError:
  1965.     if (sock != -1) {
  1966.         close(sock);
  1967.     }
  1968.     if (interp != NULL) {
  1969.     Tcl_AppendResult(interp, "couldn't open socket: ",
  1970.         Tcl_PosixError(interp), (char *) NULL);
  1971.     }
  1972.     return NULL;
  1973. }
  1974.  
  1975. /*
  1976.  *----------------------------------------------------------------------
  1977.  *
  1978.  * CreateSocketAddress --
  1979.  *
  1980.  *    This function initializes a sockaddr structure for a host and port.
  1981.  *
  1982.  * Results:
  1983.  *    1 if the host was valid, 0 if the host could not be converted to
  1984.  *    an IP address.
  1985.  *
  1986.  * Side effects:
  1987.  *    Fills in the *sockaddrPtr structure.
  1988.  *
  1989.  *----------------------------------------------------------------------
  1990.  */
  1991.  
  1992. static int
  1993. CreateSocketAddress(sockaddrPtr, host, port)
  1994.     struct sockaddr_in *sockaddrPtr;    /* Socket address */
  1995.     char *host;                /* Host.  NULL implies INADDR_ANY */
  1996.     int port;                /* Port number */
  1997. {
  1998.     struct hostent *hostent;        /* Host database entry */
  1999.     struct in_addr addr;        /* For 64/32 bit madness */
  2000.  
  2001.     (void) memset((VOID *) sockaddrPtr, '\0', sizeof(struct sockaddr_in));
  2002.     sockaddrPtr->sin_family = AF_INET;
  2003.     sockaddrPtr->sin_port = htons((unsigned short) (port & 0xFFFF));
  2004.     if (host == NULL) {
  2005.     addr.s_addr = INADDR_ANY;
  2006.     } else {
  2007.         addr.s_addr = inet_addr(host);
  2008.         if (addr.s_addr == -1) {
  2009.             hostent = gethostbyname(host);
  2010.             if (hostent != NULL) {
  2011.                 memcpy((VOID *) &addr,
  2012.                         (VOID *) hostent->h_addr_list[0],
  2013.                         (size_t) hostent->h_length);
  2014.             } else {
  2015. #ifdef    EHOSTUNREACH
  2016.                 errno = EHOSTUNREACH;
  2017. #else
  2018. #ifdef ENXIO
  2019.                 errno = ENXIO;
  2020. #endif
  2021. #endif
  2022.                 return 0;    /* error */
  2023.             }
  2024.         }
  2025.     }
  2026.         
  2027.     /*
  2028.      * NOTE: On 64 bit machines the assignment below is rumored to not
  2029.      * do the right thing. Please report errors related to this if you
  2030.      * observe incorrect behavior on 64 bit machines such as DEC Alphas.
  2031.      * Should we modify this code to do an explicit memcpy?
  2032.      */
  2033.  
  2034.     sockaddrPtr->sin_addr.s_addr = addr.s_addr;
  2035.     return 1;    /* Success. */
  2036. }
  2037.  
  2038. /*
  2039.  *----------------------------------------------------------------------
  2040.  *
  2041.  * Tcl_OpenTcpClient --
  2042.  *
  2043.  *    Opens a TCP client socket and creates a channel around it.
  2044.  *
  2045.  * Results:
  2046.  *    The channel or NULL if failed.  An error message is returned
  2047.  *    in the interpreter on failure.
  2048.  *
  2049.  * Side effects:
  2050.  *    Opens a client socket and creates a new channel.
  2051.  *
  2052.  *----------------------------------------------------------------------
  2053.  */
  2054.  
  2055. Tcl_Channel
  2056. Tcl_OpenTcpClient(interp, port, host, myaddr, myport, async)
  2057.     Tcl_Interp *interp;            /* For error reporting; can be NULL. */
  2058.     int port;                /* Port number to open. */
  2059.     char *host;                /* Host on which to open port. */
  2060.     char *myaddr;            /* Client-side address */
  2061.     int myport;                /* Client-side port */
  2062.     int async;                /* If nonzero, attempt to do an
  2063.                                          * asynchronous connect. Otherwise
  2064.                                          * we do a blocking connect. */
  2065. {
  2066.     TcpState *statePtr;
  2067.     char channelName[20];
  2068.  
  2069.     /*
  2070.      * Create a new client socket and wrap it in a channel.
  2071.      */
  2072.  
  2073.     statePtr = CreateSocket(interp, port, host, 0, myaddr, myport, async);
  2074.     if (statePtr == NULL) {
  2075.     return NULL;
  2076.     }
  2077.  
  2078.     statePtr->acceptProc = NULL;
  2079.     statePtr->acceptProcData = (ClientData) NULL;
  2080.  
  2081.     sprintf(channelName, "sock%d", statePtr->fd);
  2082.  
  2083.     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  2084.             (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
  2085.     if (Tcl_SetChannelOption(interp, statePtr->channel, "-translation",
  2086.         "auto crlf") == TCL_ERROR) {
  2087.         Tcl_Close((Tcl_Interp *) NULL, statePtr->channel);
  2088.         return NULL;
  2089.     }
  2090.     return statePtr->channel;
  2091. }
  2092.  
  2093. /*
  2094.  *----------------------------------------------------------------------
  2095.  *
  2096.  * Tcl_MakeTcpClientChannel --
  2097.  *
  2098.  *    Creates a Tcl_Channel from an existing client TCP socket.
  2099.  *
  2100.  * Results:
  2101.  *    The Tcl_Channel wrapped around the preexisting TCP socket.
  2102.  *
  2103.  * Side effects:
  2104.  *    None.
  2105.  *
  2106.  *----------------------------------------------------------------------
  2107.  */
  2108.  
  2109. Tcl_Channel
  2110. Tcl_MakeTcpClientChannel(sock)
  2111.     ClientData sock;        /* The socket to wrap up into a channel. */
  2112. {
  2113.     TcpState *statePtr;
  2114.     char channelName[20];
  2115.  
  2116.     statePtr = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
  2117.     statePtr->fd = (int) sock;
  2118.     statePtr->acceptProc = NULL;
  2119.     statePtr->acceptProcData = (ClientData) NULL;
  2120.  
  2121.     sprintf(channelName, "sock%d", statePtr->fd);
  2122.     
  2123.     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  2124.             (ClientData) statePtr, (TCL_READABLE | TCL_WRITABLE));
  2125.     if (Tcl_SetChannelOption((Tcl_Interp *) NULL, statePtr->channel,
  2126.         "-translation", "auto crlf") == TCL_ERROR) {
  2127.         Tcl_Close((Tcl_Interp *) NULL, statePtr->channel);
  2128.         return NULL;
  2129.     }
  2130.     return statePtr->channel;
  2131. }
  2132.  
  2133. /*
  2134.  *----------------------------------------------------------------------
  2135.  *
  2136.  * Tcl_OpenTcpServer --
  2137.  *
  2138.  *    Opens a TCP server socket and creates a channel around it.
  2139.  *
  2140.  * Results:
  2141.  *    The channel or NULL if failed. If an error occurred, an
  2142.  *    error message is left in interp->result if interp is
  2143.  *    not NULL.
  2144.  *
  2145.  * Side effects:
  2146.  *    Opens a server socket and creates a new channel.
  2147.  *
  2148.  *----------------------------------------------------------------------
  2149.  */
  2150.  
  2151. Tcl_Channel
  2152. Tcl_OpenTcpServer(interp, port, myHost, acceptProc, acceptProcData)
  2153.     Tcl_Interp *interp;            /* For error reporting - may be
  2154.                                          * NULL. */
  2155.     int port;                /* Port number to open. */
  2156.     char *myHost;            /* Name of local host. */
  2157.     Tcl_TcpAcceptProc *acceptProc;    /* Callback for accepting connections
  2158.                                          * from new clients. */
  2159.     ClientData acceptProcData;        /* Data for the callback. */
  2160. {
  2161.     TcpState *statePtr;
  2162.     char channelName[20];
  2163.  
  2164.     /*
  2165.      * Create a new client socket and wrap it in a channel.
  2166.      */
  2167.  
  2168.     statePtr = CreateSocket(interp, port, myHost, 1, NULL, 0, 0);
  2169.     if (statePtr == NULL) {
  2170.     return NULL;
  2171.     }
  2172.  
  2173.     statePtr->acceptProc = acceptProc;
  2174.     statePtr->acceptProcData = acceptProcData;
  2175.  
  2176.     /*
  2177.      * Set up the callback mechanism for accepting connections
  2178.      * from new clients.
  2179.      */
  2180.  
  2181.     Tcl_CreateFileHandler(statePtr->fd, TCL_READABLE, TcpAccept,
  2182.             (ClientData) statePtr);
  2183.     sprintf(channelName, "sock%d", statePtr->fd);
  2184.     statePtr->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  2185.             (ClientData) statePtr, 0);
  2186.     return statePtr->channel;
  2187. }
  2188.  
  2189. /*
  2190.  *----------------------------------------------------------------------
  2191.  *
  2192.  * TcpAccept --
  2193.  *    Accept a TCP socket connection.  This is called by the event loop.
  2194.  *
  2195.  * Results:
  2196.  *    None.
  2197.  *
  2198.  * Side effects:
  2199.  *    Creates a new connection socket. Calls the registered callback
  2200.  *    for the connection acceptance mechanism.
  2201.  *
  2202.  *----------------------------------------------------------------------
  2203.  */
  2204.  
  2205.     /* ARGSUSED */
  2206. static void
  2207. TcpAccept(data, mask)
  2208.     ClientData data;            /* Callback token. */
  2209.     int mask;                /* Not used. */
  2210. {
  2211.     TcpState *sockState;        /* Client data of server socket. */
  2212.     int newsock;            /* The new client socket */
  2213.     TcpState *newSockState;        /* State for new socket. */
  2214.     struct sockaddr_in addr;        /* The remote address */
  2215.     int len;                /* For accept interface */
  2216.     char channelName[20];
  2217.  
  2218.     sockState = (TcpState *) data;
  2219.  
  2220.     len = sizeof(struct sockaddr_in);
  2221.     newsock = accept(sockState->fd, (struct sockaddr *)&addr, &len);
  2222.     if (newsock < 0) {
  2223.         return;
  2224.     }
  2225.  
  2226.     /*
  2227.      * Set close-on-exec flag to prevent the newly accepted socket from
  2228.      * being inherited by child processes.
  2229.      */
  2230.  
  2231.     (void) fcntl(newsock, F_SETFD, FD_CLOEXEC);
  2232.     
  2233.     newSockState = (TcpState *) ckalloc((unsigned) sizeof(TcpState));
  2234.  
  2235.     newSockState->flags = 0;
  2236.     newSockState->fd = newsock;
  2237.     newSockState->acceptProc = (Tcl_TcpAcceptProc *) NULL;
  2238.     newSockState->acceptProcData = (ClientData) NULL;
  2239.         
  2240.     sprintf(channelName, "sock%d", newsock);
  2241.     newSockState->channel = Tcl_CreateChannel(&tcpChannelType, channelName,
  2242.         (ClientData) newSockState, (TCL_READABLE | TCL_WRITABLE));
  2243.  
  2244.     Tcl_SetChannelOption((Tcl_Interp *) NULL, newSockState->channel,
  2245.         "-translation", "auto crlf");
  2246.  
  2247.     if (sockState->acceptProc != (Tcl_TcpAcceptProc *) NULL) {
  2248.     (sockState->acceptProc) (sockState->acceptProcData,
  2249.         newSockState->channel, inet_ntoa(addr.sin_addr),
  2250.         ntohs(addr.sin_port));
  2251.     }
  2252. }
  2253.  
  2254. /*
  2255.  *----------------------------------------------------------------------
  2256.  *
  2257.  * TclGetDefaultStdChannel --
  2258.  *
  2259.  *    Creates channels for standard input, standard output or standard
  2260.  *    error output if they do not already exist.
  2261.  *
  2262.  * Results:
  2263.  *    Returns the specified default standard channel, or NULL.
  2264.  *
  2265.  * Side effects:
  2266.  *    May cause the creation of a standard channel and the underlying
  2267.  *    file.
  2268.  *
  2269.  *----------------------------------------------------------------------
  2270.  */
  2271.  
  2272. Tcl_Channel
  2273. TclGetDefaultStdChannel(type)
  2274.     int type;            /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR. */
  2275. {
  2276.     Tcl_Channel channel = NULL;
  2277.     int fd = 0;            /* Initializations needed to prevent */
  2278.     int mode = 0;        /* compiler warning (used before set). */
  2279.     char *bufMode = NULL;
  2280.  
  2281.     switch (type) {
  2282.         case TCL_STDIN:
  2283.             if ((lseek(0, (off_t) 0, SEEK_CUR) == -1) &&
  2284.                     (errno == EBADF)) {
  2285.                 return (Tcl_Channel) NULL;
  2286.             }
  2287.         fd = 0;
  2288.         mode = TCL_READABLE;
  2289.             bufMode = "line";
  2290.             break;
  2291.         case TCL_STDOUT:
  2292.             if ((lseek(1, (off_t) 0, SEEK_CUR) == -1) &&
  2293.                     (errno == EBADF)) {
  2294.                 return (Tcl_Channel) NULL;
  2295.             }
  2296.         fd = 1;
  2297.         mode = TCL_WRITABLE;
  2298.             bufMode = "line";
  2299.             break;
  2300.         case TCL_STDERR:
  2301.             if ((lseek(2, (off_t) 0, SEEK_CUR) == -1) &&
  2302.                     (errno == EBADF)) {
  2303.                 return (Tcl_Channel) NULL;
  2304.             }
  2305.         fd = 2;
  2306.         mode = TCL_WRITABLE;
  2307.         bufMode = "none";
  2308.             break;
  2309.     default:
  2310.         panic("TclGetDefaultStdChannel: Unexpected channel type");
  2311.         break;
  2312.     }
  2313.  
  2314.     channel = Tcl_MakeFileChannel((ClientData) fd, mode);
  2315.  
  2316.     /*
  2317.      * Set up the normal channel options for stdio handles.
  2318.      */
  2319.  
  2320.     Tcl_SetChannelOption(NULL, channel, "-translation", "auto");
  2321.     Tcl_SetChannelOption(NULL, channel, "-buffering", bufMode);
  2322.     return channel;
  2323. }
  2324.  
  2325. /*
  2326.  *----------------------------------------------------------------------
  2327.  *
  2328.  * Tcl_GetOpenFile --
  2329.  *
  2330.  *    Given a name of a channel registered in the given interpreter,
  2331.  *    returns a FILE * for it.
  2332.  *
  2333.  * Results:
  2334.  *    A standard Tcl result. If the channel is registered in the given
  2335.  *    interpreter and it is managed by the "file" channel driver, and
  2336.  *    it is open for the requested mode, then the output parameter
  2337.  *    filePtr is set to a FILE * for the underlying file. On error, the
  2338.  *    filePtr is not set, TCL_ERROR is returned and an error message is
  2339.  *    left in interp->result.
  2340.  *
  2341.  * Side effects:
  2342.  *    May invoke fdopen to create the FILE * for the requested file.
  2343.  *
  2344.  *----------------------------------------------------------------------
  2345.  */
  2346.  
  2347. int
  2348. Tcl_GetOpenFile(interp, string, forWriting, checkUsage, filePtr)
  2349.     Tcl_Interp *interp;        /* Interpreter in which to find file. */
  2350.     char *string;        /* String that identifies file. */
  2351.     int forWriting;        /* 1 means the file is going to be used
  2352.                  * for writing, 0 means for reading. */
  2353.     int checkUsage;        /* 1 means verify that the file was opened
  2354.                  * in a mode that allows the access specified
  2355.                  * by "forWriting". Ignored, we always
  2356.                                  * check that the channel is open for the
  2357.                                  * requested mode. */
  2358.     ClientData *filePtr;    /* Store pointer to FILE structure here. */
  2359. {
  2360.     Tcl_Channel chan;
  2361.     int chanMode;
  2362.     Tcl_ChannelType *chanTypePtr;
  2363.     int fd;
  2364.     FILE *f;
  2365.     
  2366.     chan = Tcl_GetChannel(interp, string, &chanMode);
  2367.     if (chan == (Tcl_Channel) NULL) {
  2368.         return TCL_ERROR;
  2369.     }
  2370.     if ((forWriting) && ((chanMode & TCL_WRITABLE) == 0)) {
  2371.         Tcl_AppendResult(interp,
  2372.                 "\"", string, "\" wasn't opened for writing", (char *) NULL);
  2373.         return TCL_ERROR;
  2374.     } else if ((!(forWriting)) && ((chanMode & TCL_READABLE) == 0)) {
  2375.         Tcl_AppendResult(interp,
  2376.                 "\"", string, "\" wasn't opened for reading", (char *) NULL);
  2377.         return TCL_ERROR;
  2378.     }
  2379.  
  2380.     /*
  2381.      * We allow creating a FILE * out of file based, pipe based and socket
  2382.      * based channels. We currently do not allow any other channel types,
  2383.      * because it is likely that stdio will not know what to do with them.
  2384.      */
  2385.  
  2386.     chanTypePtr = Tcl_GetChannelType(chan);
  2387.     if ((chanTypePtr == &fileChannelType) || (chanTypePtr == &tcpChannelType)
  2388.         || (strcmp(chanTypePtr->typeName, "pipe") == 0)) {
  2389.         if (Tcl_GetChannelHandle(chan,
  2390.         (forWriting ? TCL_WRITABLE : TCL_READABLE), (ClientData*) &fd)
  2391.         == TCL_OK) {
  2392.  
  2393.         /*
  2394.          * The call to fdopen below is probably dangerous, since it will
  2395.          * truncate an existing file if the file is being opened
  2396.          * for writing....
  2397.          */
  2398.         
  2399.         f = fdopen(fd, (forWriting ? "w" : "r"));
  2400.         if (f == NULL) {
  2401.         Tcl_AppendResult(interp, "cannot get a FILE * for \"", string,
  2402.             "\"", (char *) NULL);
  2403.         return TCL_ERROR;
  2404.         }
  2405.         *filePtr = (ClientData) f;
  2406.         return TCL_OK;
  2407.     }
  2408.     }
  2409.  
  2410.     Tcl_AppendResult(interp, "\"", string,
  2411.             "\" cannot be used to get a FILE *", (char *) NULL);
  2412.     return TCL_ERROR;        
  2413. }
  2414.  
  2415. /*
  2416.  *----------------------------------------------------------------------
  2417.  *
  2418.  * TclUnixWaitForFile --
  2419.  *
  2420.  *    This procedure waits synchronously for a file to become readable
  2421.  *    or writable, with an optional timeout.
  2422.  *
  2423.  * Results:
  2424.  *    The return value is an OR'ed combination of TCL_READABLE,
  2425.  *    TCL_WRITABLE, and TCL_EXCEPTION, indicating the conditions
  2426.  *    that are present on file at the time of the return.  This
  2427.  *    procedure will not return until either "timeout" milliseconds
  2428.  *    have elapsed or at least one of the conditions given by mask
  2429.  *    has occurred for file (a return value of 0 means that a timeout
  2430.  *    occurred).  No normal events will be serviced during the
  2431.  *    execution of this procedure.
  2432.  *
  2433.  * Side effects:
  2434.  *    Time passes.
  2435.  *
  2436.  *----------------------------------------------------------------------
  2437.  */
  2438.  
  2439. int
  2440. TclUnixWaitForFile(fd, mask, timeout)
  2441.     int fd;            /* Handle for file on which to wait. */
  2442.     int mask;            /* What to wait for: OR'ed combination of
  2443.                  * TCL_READABLE, TCL_WRITABLE, and
  2444.                  * TCL_EXCEPTION. */
  2445.     int timeout;        /* Maximum amount of time to wait for one
  2446.                  * of the conditions in mask to occur, in
  2447.                  * milliseconds.  A value of 0 means don't
  2448.                  * wait at all, and a value of -1 means
  2449.                  * wait forever. */
  2450. {
  2451.     Tcl_Time abortTime, now;
  2452.     struct timeval blockTime, *timeoutPtr;
  2453.     int index, bit, numFound, result = 0;
  2454.     static fd_mask readyMasks[3*MASK_SIZE];
  2455.                 /* This array reflects the readable/writable
  2456.                  * conditions that were found to exist by the
  2457.                  * last call to select. */
  2458.  
  2459.     /*
  2460.      * If there is a non-zero finite timeout, compute the time when
  2461.      * we give up.
  2462.      */
  2463.  
  2464.     if (timeout > 0) {
  2465.     TclpGetTime(&now);
  2466.     abortTime.sec = now.sec + timeout/1000;
  2467.     abortTime.usec = now.usec + (timeout%1000)*1000;
  2468.     if (abortTime.usec >= 1000000) {
  2469.         abortTime.usec -= 1000000;
  2470.         abortTime.sec += 1;
  2471.     }
  2472.     timeoutPtr = &blockTime;
  2473.     } else if (timeout == 0) {
  2474.     timeoutPtr = &blockTime;
  2475.     blockTime.tv_sec = 0;
  2476.     blockTime.tv_usec = 0;
  2477.     } else {
  2478.     timeoutPtr = NULL;
  2479.     }
  2480.  
  2481.     /*
  2482.      * Initialize the ready masks and compute the mask offsets.
  2483.      */
  2484.  
  2485.     if (fd >= FD_SETSIZE) {
  2486.     panic("TclWaitForFile can't handle file id %d", fd);
  2487.     }
  2488.     memset((VOID *) readyMasks, 0, 3*MASK_SIZE*sizeof(fd_mask));
  2489.     index = fd/(NBBY*sizeof(fd_mask));
  2490.     bit = 1 << (fd%(NBBY*sizeof(fd_mask)));
  2491.     
  2492.     /*
  2493.      * Loop in a mini-event loop of our own, waiting for either the
  2494.      * file to become ready or a timeout to occur.
  2495.      */
  2496.  
  2497.     while (1) {
  2498.     if (timeout > 0) {
  2499.         blockTime.tv_sec = abortTime.sec - now.sec;
  2500.         blockTime.tv_usec = abortTime.usec - now.usec;
  2501.         if (blockTime.tv_usec < 0) {
  2502.         blockTime.tv_sec -= 1;
  2503.         blockTime.tv_usec += 1000000;
  2504.         }
  2505.         if (blockTime.tv_sec < 0) {
  2506.         blockTime.tv_sec = 0;
  2507.         blockTime.tv_usec = 0;
  2508.         }
  2509.     }
  2510.     
  2511.     /*
  2512.      * Set the appropriate bit in the ready masks for the fd.
  2513.      */
  2514.  
  2515.     if (mask & TCL_READABLE) {
  2516.         readyMasks[index] |= bit;
  2517.     }
  2518.     if (mask & TCL_WRITABLE) {
  2519.         (readyMasks+MASK_SIZE)[index] |= bit;
  2520.     }
  2521.     if (mask & TCL_EXCEPTION) {
  2522.         (readyMasks+2*(MASK_SIZE))[index] |= bit;
  2523.     }
  2524.  
  2525.     /*
  2526.      * Wait for the event or a timeout.
  2527.      */
  2528.  
  2529.     numFound = select(fd+1, (SELECT_MASK *) &readyMasks[0],
  2530.         (SELECT_MASK *) &readyMasks[MASK_SIZE],
  2531.         (SELECT_MASK *) &readyMasks[2*MASK_SIZE], timeoutPtr);
  2532.     if (numFound == 1) {
  2533.         if (readyMasks[index] & bit) {
  2534.         result |= TCL_READABLE;
  2535.         }
  2536.         if ((readyMasks+MASK_SIZE)[index] & bit) {
  2537.         result |= TCL_WRITABLE;
  2538.         }
  2539.         if ((readyMasks+2*(MASK_SIZE))[index] & bit) {
  2540.         result |= TCL_EXCEPTION;
  2541.         }
  2542.         result &= mask;
  2543.         if (result) {
  2544.         break;
  2545.         }
  2546.     }
  2547.     if (timeout == 0) {
  2548.         break;
  2549.     }
  2550.  
  2551.     /*
  2552.      * The select returned early, so we need to recompute the timeout.
  2553.      */
  2554.  
  2555.     TclpGetTime(&now);
  2556.     if ((abortTime.sec < now.sec)
  2557.         || ((abortTime.sec == now.sec)
  2558.         && (abortTime.usec <= now.usec))) {
  2559.         break;
  2560.     }
  2561.     }
  2562.     return result;
  2563. }
  2564.